package com.k8.common.aop;

import com.k8.common.annotation.Auto;
import com.k8.common.aop.internal.advisor.Advisor;
import com.k8.common.aop.internal.exception.PointCutResolveException;
import com.k8.common.factroy.loader.ExtensionAccessor;
import com.k8.common.factroy.loader.ExtensionLoader;
import com.k8.common.ioc.BeanPostProcessor;
import com.k8.common.util.AopUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Auto
@Slf4j
public class SmartInstantiationBeanPostProcessor extends AdvisorScanner implements BeanPostProcessor {

    private List<Advisor> cacheAdvisors;
    private final String proxyType;
    private ProxyFactory proxyFactory;
    private boolean hasScan;

    public SmartInstantiationBeanPostProcessor(String proxyType) {
        this.proxyType = proxyType;
    }

    /**
     * 扫描AspectConfig的实现类
     */
    @Override
    public void scanAspectjImpl(ExtensionAccessor extensionAccessor) {
        if (!hasScan) {
            ExtensionLoader<ProxyFactory> proxyFactoryExtensionLoader = extensionAccessor.getExtensionLoader(ProxyFactory.class);
            this.proxyFactory = proxyFactoryExtensionLoader.getExtension(proxyType);
            if (this.proxyFactory == null) {
                this.proxyFactory = proxyFactoryExtensionLoader.getExtension(AopConstants.JdkAspect);
                if (this.proxyFactory == null) {
                    throw new RuntimeException("ProxyFactory is not present");
                }
            }
            this.cacheAdvisors = doScanAdvisor(extensionAccessor);
            hasScan = true;
        }
    }

    @Override
    List<Advisor> doScanAdvisor(ExtensionAccessor extensionAccessor) {
        List<AspectConfig> aspectConfigs = extensionAccessor.getExtensionLoader(AspectConfig.class).getExtensions();
        return aspectConfigs.stream()
                .parallel()
                .flatMap(aspectConfig -> {
                    Class<? extends AspectConfig> clazz = aspectConfig.getClass();
                    Method[] methods = clazz.getMethods();
                    return Arrays.stream(methods).map(method -> {
                        Advisor advisor = null;
                        try {
                            advisor = AopUtil.methodToAdvisor(aspectConfig, method);
                        } catch (PointCutResolveException e) {
                            log.error("Error PointCut signature in Class:{},Method:{},signature:{}", clazz.getName(), method.getName(), e.getMessage());
                        }
                        return advisor;
                    }).filter(Objects::nonNull);
                }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    @Nullable
    @Override
    public <E> E postProcessBeforeInitialization(E bean, String beanName) {
        return bean;
    }

    @Nullable
    @Override
    public <E> E postProcessAfterInitialization(E bean, String beanName) {
        if (bean instanceof ProxyFactory || cacheAdvisors == null || cacheAdvisors.isEmpty()) return bean;
        return (E) proxyFactory.build(bean, cacheAdvisors);
    }
}


